/* ========================================================================= */
/* ------------------------------------------------------------------------- */
/*!				\file		CC_PlugIn.h
 \date		june 2010
 \author		TNick

 \brief		Contains the definition of CC_PlugIn class and related


 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Please read ReadMe.txt and Licence.txt in root folder
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


*/
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
#ifndef		__CC_PLUGIN_INC__
#define		__CC_PLUGIN_INC__
#ifdef		__cplusplus
//
//
//
//
/*  INCLUDES    ------------------------------------------------------------ */

class		CC_PlgInTpl;
class		CC_PlugIn;
class		CC_Cmd;

#include	<QString>
#include	<QPluginLoader>
#include	<QTime>

#include	<generic/virtype.h>
#include	<generic/cc_l2dn.h>
#include	<base/CC_Err.h>

#include	"CC_PlgInTpl.h"

/*  INCLUDES    ============================================================ */
//
//
//
//
//
//
//
//
/*  CLASS    --------------------------------------------------------------- */

/// this class simply envelops a plug-in
/**
*	It is needed to differentiate between commands and plug-ins.
*	Each plug-in in the model creates a QModelIndex that has
*	it's position in chain (row) and a pointer to an instance of this type.
*	Each command is represented by a pointer to parent plugin and
*	the row is used to identify commands.
*/
class	PlgEnvel : public VirType
{

	//
	//
	//
	//
	/*  DEFINITIONS    ----------------------------------------------------- */

public:

	/// types used with VirType; if used in broader context, change !!!
	enum	PLGTYPE	{
		TY_ENVELOPE,
		TY_PLUGIN
	};

	/*  DEFINITIONS    ===================================================== */
	//
	//
	//
	//
	/*  DATA    ------------------------------------------------------------ */

private:
	/// store the pointer for future reference
	CC_PlugIn *		p_plg_in;

	/*  DATA    ============================================================ */
	//
	//
	//
	//
	/*  FUNCTIONS    ------------------------------------------------------- */
public:

	virtual ~PlgEnvel() {}

	/// identify yourself
	virtual int			objectType		(void) { return TY_ENVELOPE; }

	/// tell what's the plug-in that you're envelopping (!)
	inline CC_PlugIn *	plgInReveal		(void) { return (p_plg_in); }

	/// identify yourself
	void				setPlgIn		(CC_PlugIn * p_new)
	{ p_plg_in = p_new; }

	/// return the index of this plug-in in the chain
	inline int			index			(void);

	/*  FUNCTIONS    ======================================================= */
	//
	//
	//
	//

};	/*	class PlgEnvel	*/

/*  CLASS    =============================================================== */
//
//
//
//
/*  CLASS    --------------------------------------------------------------- */

/// Class representing a plug-in module
/**
*	The CC_L2DN class binds this in chain but does not store any pointers
*	regarding the father. @n
*	The name stored inside CC_L2DN class is either the name of the file without
*	extension and without eventual "PlugIn_" prefix. This name needs to be
*	unique (one can't load a plug-in from a diffrent path but with same name).@n
*	Full path of the file from where the plug-in was loaded is stored in a
*	separate place for future reference. @n
*
*	Each plug-in recives and stores in this class an ID that is unique among
*	other plug-ins. This ID is limited to 65535 and this is the theoretical
*	maximum number of plugins that may be loaded at any given time.@n
*	Because of the nature of this ID, code that needs to identify the plug-in
*	beyond a session should use plug-in's name.
*/
class	CC_PlugIn	: public CC_L2DN, public VirType {


	//
	//
	//
	//
	/*  DEFINITIONS    --------------------------------------------------------- */

	/// maximum number of elements that we can load
#define MAX_PLUGINS			0xFFFF
	/// special marker for "core module"
#define	CORE_MODULE_ID		0

	/*  DEFINITIONS    ========================================================= */
	//
	//
	//
	//
	/*  DATA    ---------------------------------------------------------------- */

	/// Qt object that hosts the plug-in
	QPluginLoader 		qt_plg;

	///  the interface (inside library)
	/**
 *	For plug-ins that are only shadows this is NULL
 */
    CC_PlgInTpl *		interf;

	/// the full path and file name for this module
	QString				s_full_path;

	/// the ID of this plug-in
	unsigned			u_id;

	/// generate ID faster
	static unsigned		u_id_base;

	/// last load time
	QTime				last_load;

	/// last unload time
	QTime				last_unload;


	/*  DATA    ================================================================ */
	//
	//
	//
	//
	/*  FUNCTIONS    ----------------------------------------------------------- */

private:
	/// constructor; only used internally; use CC_PlugIn::load()
	CC_PlugIn(
		QString			s_name,	/**< the name for the module */
		QString			s_file,	/**< fully qualified path to module */
		CC_Err*			p_err	/**< error correlation structure */
		);

public:

	/* static functions used for management purposes */

	/// function used to initialize the plug-in system
	/**
 *	This is the place where all plug-ins are iterated
 *	and loaded when the application starts; to check
 *	if new plug-ins were added to plug-in folder, use
 *	refreshPlugInSystem method
 */
    static CCERR::EERRORS	startPlugInSystem(
        CC_Err*			p_err	/**< error correlation structure */
        );

	/// checks if any plug-ins were added to plug-in folder
	/**
 *	if it finds some, loads them at this time
 */
    static CCERR::EERRORS	refreshPlugInSystem(
        unsigned *		p_new,	/**< number of newly added plug-ins */
        CC_Err*			p_err	/**< error correlation structure */
        );

	/// unloads all plug-ins
	/**
 *	durring this call end() routines for all loaded plug-ins
 *	are called
 */
    static CCERR::EERRORS	terminatePlugInSystem(
        CC_Err*			p_err	/**< error correlation structure */
        );

	/// loads a plug-in from a file and creates a new CC_PlugIn for it
	static CCERR::EERRORS	load			(
		QString			s_file,	/**< path and file name */
		CC_PlugIn**		p_plg,	/**< resulted object, if any */
		CC_Err*			p_err	/**< error correlation structure */
		);





	/// destructor; will free resources;
	virtual					~CC_PlugIn		()
	{
		if (!isShadow())
			{
				CC_Err	lc_err;
				terminate(&lc_err);
			}

		qt_plg.unload();
	}

	/// return an number that identifies you
	virtual int				objectType	(void)
	{ return PlgEnvel::TY_PLUGIN; }

	/// retrieve the ID of this Plug-In
	inline unsigned			id				(void)
	{
		return u_id;
	}

	/// retrieve the full path and name of this plug-in
	inline QString			path			(void)
	{
		return s_full_path;
	}

	/// tell if this is a shadow or a functional plug-in
	inline bool				isShadow		(void)
	{
		return (interf == NULL);
	}

	/// tell if initialization went ok
	inline bool				isOK			(void)
	{
		return (u_id != CORE_MODULE_ID);
	}

	/// will load itself from a file
	CCERR::EERRORS			reinit			(
		QString			s_file,	/**< fully qualified path to module */
		CC_Err*			p_err	/**< error correlation structure */
		);

	/// will unload the plugin leaving a shadow behind
	void					terminate		(
		CC_Err*			p_err	/**< error correlation structure */
		);



	/// as we know the father of this chain, we can further simplify
	int						index		(void) const;




	/* mirrors for contained interface */

	/// function that tells how manny commands this module stores
	/**
 *	if the module does not export any commands to the user, this is ok to
 *	be 0;
 *
 *	@return		number of items or 0 if none
 */
    inline unsigned			countCmd			(void)
    { if (interf == NULL) return 0; return interf->countCmd(); }

	/// function that exports internal array of commands
	/**
 *	if the module does not export any commands to the user, this is ok to
 *	be NULL;
 *
 *	@return		a valid pointer or NULL
 */
    inline CC_Cmd*			arrayCmd			(void)
    { if (interf == NULL) return NULL; return interf->arrayCmd(); }






private:
	/// scans for a usable ID
	static unsigned			getFreeID		(void);

	/// gets a plug-in name from a full path and name
	static QString			getNameFromPath	(QString s_file);

	/*  FUNCTIONS    =========================================================== */
	//
	//
	//
	//

};	/*	class	CC_PlugIn	*/
//
//
//
//




	/// return the index of this plug-in in the chain
	inline int			PlgEnvel::index			(void)
	{	return p_plg_in->index();	}







#endif	//	__cplusplus
#endif	//	__CC_PLUGIN_INC__
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
